package com.app.GoCuro.util;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

import com.app.GoCuro.ScannedContents;

/*
 * 
 * Methods which provide interface to the database
 * 
 */
public class CustomHttpClient {
	// the current context of the application
	Context currContext;

	// variables for configuring the http client
	private static HttpClient ourHttpClient;
	private final static int HTTP_TIMEOUT = 30 * 1000;

	// common strings for accessing the web client
	private String baseUrl = "http://gocuro.herokuapp.com/";
	private String loginUrl = "login?";
	private String jobsitesUrl = "jobsites/get_sites";
	private String getItemsUrl = "items/get_inventory/";

	// error strings
	private String loginError = "Invalid login credentials.";
	private String serverConnectionError = "Error connecting to the server.";
	private String databaseConnectionError = "Error parsing out databases";
	private String databaseItemRetrieveError = "Error retrieving items for the database";
	private String connectionError = "Network connectivity not available.";

	// JSON fields
	private String statusJSON = "status";
	private String jobsiteJSON = "jobsitename";
	private String jobsiteIdJSON = "jobsiteid";
	private String nameJSON = "name";
	private String updatedJSON = "updated_at";
	private String idJSON = "id";
	private String barcodeJSON = "barcode_id";

	/*
	 * Constructor takes an instance of an activity for testing connectivity and
	 * raising dialogs
	 */
	public CustomHttpClient(Context context) {
		currContext = context;
	}

	/*
	 * Get the instance of the HttpClient, lazily instantiated to create only
	 * once
	 */
	private static HttpClient getHttpClient() {
		// create the client if it does not exist
		if (ourHttpClient == null) {
			ourHttpClient = new DefaultHttpClient();
			final HttpParams params = ourHttpClient.getParams();
			HttpConnectionParams.setConnectionTimeout(params, HTTP_TIMEOUT);
			HttpConnectionParams.setSoTimeout(params, HTTP_TIMEOUT);
			ConnManagerParams.setTimeout(params, HTTP_TIMEOUT);
		}
		return ourHttpClient;
	}

	/*
	 * Attempts to log into the database from the phone client
	 * 
	 * @param String username the username to use to login
	 * 
	 * @param String password the password for the user for logging in
	 * 
	 * @return a login response containing information about the user
	 */
	public LoginResponse login(String username, String password) {
		if (!isOnline())
			return null;

		HttpClient client = getHttpClient();
		String requestUrl;
		requestUrl = String.format(baseUrl + loginUrl + "username=%s&pwd=%s",
				username, password);
		HttpPost get = new HttpPost(requestUrl);

		try {
			HttpResponse response = client.execute(get);
			// act on a valid http response
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				DataInputStream in = new DataInputStream(response.getEntity()
						.getContent());
				JSONObject retJSON = new JSONObject(in.readLine());
				String JSONstatusCode = retJSON.getString(statusJSON);

				if (JSONstatusCode.equals("200")) {
					String defaultJobsite = retJSON.getString(jobsiteJSON);
					String jobsiteId = retJSON.getString(jobsiteIdJSON);
					LoginResponse loginResponse = new LoginResponse(
							defaultJobsite, jobsiteId, username, password);
					return loginResponse;
				} else {
					ErrorMessageCreator.CreateErrorMessage(currContext,
							loginError);
					return null;
				}
			} else {
				ErrorMessageCreator.CreateErrorMessage(currContext,
						serverConnectionError);
				return null;
			}
		} catch (IOException e) {
			ErrorMessageCreator.CreateErrorMessage(currContext, loginError);
			return null;
		} catch (JSONException e) {
			ErrorMessageCreator.CreateErrorMessage(currContext, loginError);
			return null;
		}
	}

	/*
	 * Gets the databases associated with a user
	 * 
	 * @return a list of objects with information about each database
	 */
	public ArrayList<DatabaseInformation> getUserDatabases() {
		if (!isOnline())
			return null;

		HttpClient client = getHttpClient();
		String requestUrl = baseUrl + jobsitesUrl;
		HttpGet get = new HttpGet(requestUrl);
		boolean exceptionCaught = false;

		try {
			HttpResponse response = client.execute(get);
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				DataInputStream in = new DataInputStream(response.getEntity()
						.getContent());
				String validResponse = in.readLine();
				JSONArray retJSON = new JSONArray(validResponse);

				ArrayList<DatabaseInformation> databases = new ArrayList<DatabaseInformation>();

				// add the name of databases to the array list
				for (int i = 0; i < retJSON.length(); i++) {
					JSONObject element = retJSON.getJSONObject(i);
					String dbName = element.getString(nameJSON);
					String dbId = element.getString(idJSON);
					String lastUpdate = element.getString(updatedJSON);

					DatabaseInformation currDatabase = new DatabaseInformation(
							dbName, dbId, lastUpdate);
					databases.add(currDatabase);
				}

				return databases;
			}

		} catch (JSONException e) {
			ErrorMessageCreator.CreateErrorMessage(currContext,
					databaseConnectionError);
			return null;
		} catch (IOException e) {
			ErrorMessageCreator.CreateErrorMessage(currContext,
					databaseConnectionError);
			return null;
		}

		ErrorMessageCreator.CreateErrorMessage(currContext,
				databaseConnectionError);
		return null;
	}

	/*
	 * gets the items associated with the current database
	 * 
	 * @param String username the username logging in to get the database
	 * 
	 * @param String password the password for the user
	 * 
	 * @param String databaseId the id of the database being queried for
	 * 
	 * @return a list of objects representing items in the database
	 */
	public ArrayList<DatabaseItem> getDatabaseItems(String username,
			String password, String databaseId) {
		if (!isOnline())
			return null;

		HttpClient client = getHttpClient();
		String requestUrl = String.format(baseUrl + getItemsUrl
				+ "%s?username=%s&pwd=%s", databaseId, username, password);
		HttpGet get = new HttpGet(requestUrl);

		try {
			HttpResponse response = client.execute(get);
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				DataInputStream in = new DataInputStream(response.getEntity()
						.getContent());
				String validResponse = in.readLine();
				JSONArray retJSON = new JSONArray(validResponse);

				ArrayList<DatabaseItem> databases = new ArrayList<DatabaseItem>();

				// add the name of databases to the array list
				for (int i = 0; i < retJSON.length(); i++) {
					JSONObject element = retJSON.getJSONObject(i);
					String barcode = element.getString(barcodeJSON);

					DatabaseItem currDatabase = new DatabaseItem(barcode);
					databases.add(currDatabase);
				}
				return databases;
			}
			ErrorMessageCreator.CreateErrorMessage(currContext,
					databaseItemRetrieveError);
			return null;
		} catch (JSONException e) {
			ErrorMessageCreator.CreateErrorMessage(currContext,
					databaseItemRetrieveError);
			return null;
		} catch (IOException e) {
			ErrorMessageCreator.CreateErrorMessage(currContext,
					databaseItemRetrieveError);
			return null;
		}
	}

	/*
	 * Posts a new item to the server for use
	 * 
	 * @param ScannedContents contents the contents of the scan to be
	 * transmitted to the server
	 */
	public void postNewItem(ScannedContents contents) {

	}

	/*
	 * Tests if a connection is available for the application
	 * 
	 * Every connection to the database does this test to ensure current
	 * operation will send to database
	 * 
	 * @return true iff connectivity is currently available
	 */
	private boolean isOnline() {
		ConnectivityManager cm = (ConnectivityManager) currContext
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo netInfo = cm.getActiveNetworkInfo();

		if (netInfo == null || !netInfo.isConnected()) {
			ErrorMessageCreator
					.CreateErrorMessage(currContext, connectionError);
			return false;
		}
		return true;
	}
}